package ranab.server.ftp.usermanager;

import java.io.File;
import java.io.IOException;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.ArrayList;
import java.util.Iterator;

import ranab.io.IoUtils;
import ranab.io.LogFile;
import ranab.util.BaseProperties;
import ranab.util.EncryptUtils;
import ranab.server.ftp.FtpConfig;

/**
 * Properties file based <code>UserManager</code>
 * implementation. We use <code>user.properties</code> file
 * to store user data.
 * 
 * @author <a href="mailto:rana_b@yahoo.com">Rana Bhattacharyya</a>
 */
public
class PropertiesUserManager extends UserManager  {

    private final static String PREFIX    = "FtpServer.user.";
    private final static String USER_PROP = "user.properties";

    private BaseProperties mUserData;
    private File       mUserDataFile;
    private boolean    mbEncrypt;
    
    private long mlLastModified;
    
    /**
     * Instantiate user manager.
     *
     * @param cfg Ftp config object.
     */
    public PropertiesUserManager(FtpConfig cfg) throws Exception {
        super(cfg);
        
        mUserDataFile = new File(mConfig.getDataDir(), USER_PROP);
        mUserDataFile.createNewFile();
        mUserData = new BaseProperties(mUserDataFile);
        mbEncrypt = cfg.getBoolean(FtpConfig.PREFIX + "prop.encrypt", false);
        mlLastModified = mUserDataFile.lastModified();
        cfg.getLogger().info("Loaded user data file " + mUserDataFile.getAbsolutePath());
    }


    /**
     * Save user data. Store the properties.
     */
    public void save(User usr) throws IOException {

       // null value check
       if(usr.getName() == null) {
           throw new NullPointerException("User name is null.");
       }
       String thisPrefix = PREFIX + usr.getName() + '.';
       
       // set other properties
       mUserData.setProperty(thisPrefix+"password", getPassword(usr));
       mUserData.setProperty(thisPrefix+"home",     usr.getVirtualDirectory().getRootDirectory());
       mUserData.setProperty(thisPrefix+"enabled",  usr.getEnabled());
       mUserData.setProperty(thisPrefix+"write",    usr.getVirtualDirectory().getWritePermission());
       mUserData.setProperty(thisPrefix+"idle",     usr.getMaxIdleTime());
       mUserData.setProperty(thisPrefix+"upload",   usr.getMaxUploadRate());
       mUserData.setProperty(thisPrefix+"download", usr.getMaxDownloadRate());
   
       // save user data
       FileOutputStream fos = null;
       try {
           fos = new FileOutputStream(mUserDataFile);
           mUserData.store(fos, "Generated file - don't edit (please)");
           mlLastModified = mUserDataFile.lastModified();
       }
       finally {
           IoUtils.close(fos);
       }
    }
     

    /**
     * Delete an user. Removes all this user entries from the properties.
     * After removing the corresponding from the properties, save the data.
     */
    public void delete(String usrName) throws IOException {
        
        // remove entries from properties
        String thisPrefix = PREFIX + usrName + '.';
        Enumeration propNames = mUserData.propertyNames();
        ArrayList remKeys = new ArrayList();
        while(propNames.hasMoreElements()) {
            String thisKey = propNames.nextElement().toString();
            if(thisKey.startsWith(thisPrefix)) {
                remKeys.add(thisKey);
            }
        }
        Iterator remKeysIt = remKeys.iterator();
        while (remKeysIt.hasNext()) {
            mUserData.remove(remKeysIt.next().toString());
        }
        
        // save user data
        FileOutputStream fos = null;
        try {    
            fos = new FileOutputStream(mUserDataFile);
            mUserData.store(fos, "Generated file - don't edit (please)");
            mlLastModified = mUserDataFile.lastModified();
        }
        finally {
            IoUtils.close(fos);
        }
    }
    
    
    /**
     * Get user password. Returns the encrypted value.
     * If the password value is not null
     *    password = new password 
     * else 
     *   if user does exist
     *     password = old password
     *   else 
     *     password = ""
     */
    private String getPassword(User usr) {
        String password = usr.getPassword();
        if (password != null) {
            if (mbEncrypt) {
                password = EncryptUtils.encryptMD5(password);
            }
        }
        else if ( doesExist(usr.getName()) ) {
            String key = PREFIX + usr.getName() + ".password";
            password = mUserData.getProperty(key, "");
        }
        
        if (password == null) {
            password = "";
        }
        
        return password;
    } 
    
    
    /**
     * Get all user names.
     */
    public Collection getAllUserNames() {

        // get all user names
        ArrayList ulst = new ArrayList();
        Enumeration allKeys = mUserData.propertyNames();
        while(allKeys.hasMoreElements()) {
            String key = (String)allKeys.nextElement();
            if(key.endsWith(".home")) {
                String name = key.substring(PREFIX.length());
                int endIndex = name.length() - ".home".length();
                name = name.substring(0, endIndex);
                ulst.add(name);
            }
        }
        
        Collections.sort(ulst);
        return ulst;
    }


    /**
     * Load user data.
     */
    public User getUserByName(String userName) {
		
        if (!doesExist(userName)) {
        	return null;
        }
        
        String baseKey = PREFIX + userName + '.';
        User user = new User();
        user.setName(userName);
        user.setEnabled(mUserData.getBoolean(baseKey + "enabled", true));
        user.getVirtualDirectory().setRootDirectory( mUserData.getFile(baseKey + "home", new File("/")) );
        user.getVirtualDirectory().setWritePermission(mUserData.getBoolean(baseKey + "write", false));
        user.setMaxIdleTime(mUserData.getInteger(baseKey + "idle", 0));
        user.setMaxUploadRate(mUserData.getInteger(baseKey + "upload", 0));
        user.setMaxDownloadRate(mUserData.getInteger(baseKey + "download", 0));
        return user;
    }
	
    
    /**
     * User existance check
     */
    public boolean doesExist(String name) {
    	String key = PREFIX + name + ".home";
        return mUserData.containsKey(key);
    }
    
    
    /**
     * User authenticate method
     */
    public boolean authenticate(String user, String password) {
        System.out.println("in prop auth");
        String passVal = mUserData.getProperty(PREFIX + user + ".password");
        if (mbEncrypt) {
            password = EncryptUtils.encryptMD5(password);
        }
        return password.equals(passVal);
    }
    
    /**
     * Reload the user data if necessary
     */
    public void reload() throws Exception {
        long lastModified = mUserDataFile.lastModified();
        if (lastModified > mlLastModified) {
            FileInputStream fis = new FileInputStream(mUserDataFile);
            mUserData.load(fis);
            fis.close();
            mlLastModified = lastModified;
            getConfig().getLogger().info("File modified - loading " + mUserDataFile.getAbsolutePath());
        }
    }
    
    /**
     * Close the user manager - remove existing entries.
     */
    public void dispose() {
        if (mUserData != null) {
            mUserData.clear();
            mUserData = null;
        }
    }
}

